package cn.gotom.svg;

import java.util.HashMap;
import java.util.LinkedList;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.log4j.Logger;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

public class SVGParser extends DefaultHandler
{
	protected final Logger log = Logger.getLogger(this.getClass());
	final HashMap<String, Class<?>> nodeClasses = new HashMap<String, Class<?>>();
	final LinkedList<SVGElement> buildStack = new LinkedList<SVGElement>();
	private SVGRoot root;
	private int skipNonSVGTagDepth = 0;

	public SVGParser()
	{
		initNodeClasses();
	}

	public SVGRoot getRoot()
	{
		return root;
	}

	private void initNodeClasses()
	{
		// nodeClasses.put("a", A.class);
		// nodeClasses.put("animate", Animate.class);
		// nodeClasses.put("animatecolor", AnimateColor.class);
		// nodeClasses.put("animatemotion", AnimateMotion.class);
		// nodeClasses.put("animatetransform", AnimateTransform.class);
		nodeClasses.put("circle", Circle.class);
		// nodeClasses.put("clippath", ClipPath.class);
		// nodeClasses.put("defs", Defs.class);
		// nodeClasses.put("desc", Desc.class);
		nodeClasses.put("ellipse", Ellipse.class);
		// nodeClasses.put("filter", Filter.class);
		// nodeClasses.put("font", Font.class);
		// nodeClasses.put("font-face", FontFace.class);
		nodeClasses.put("g", Group.class);
		// nodeClasses.put("glyph", Glyph.class);
		// nodeClasses.put("hkern", Hkern.class);
		// nodeClasses.put("image", ImageSVG.class);
		nodeClasses.put("line", Line.class);
		// nodeClasses.put("lineargradient", LinearGradient.class);
		// nodeClasses.put("marker", Marker.class);
		// nodeClasses.put("metadata", Metadata.class);
		// nodeClasses.put("missing-glyph", MissingGlyph.class);
		nodeClasses.put("path", PathElement.class);
		// nodeClasses.put("pattern", PatternSVG.class);
		nodeClasses.put("polygon", Polygon.class);
		nodeClasses.put("polyline", Polyline.class);
		// nodeClasses.put("radialgradient", RadialGradient.class);
		nodeClasses.put("rect", Rect.class);
		// nodeClasses.put("set", SetSmil.class);
		// nodeClasses.put("shape", ShapeElement.class);
		// nodeClasses.put("stop", Stop.class);
		// nodeClasses.put("style", Style.class);
		nodeClasses.put("svg", SVGRoot.class);
		// nodeClasses.put("symbol", Symbol.class);
		// nodeClasses.put("text", Text.class);
		// nodeClasses.put("title", Title.class);
		// nodeClasses.put("tspan", Tspan.class);
		// nodeClasses.put("use", Use.class);

	}

	public void parse(InputSource inputSource) throws SVGException
	{
		try
		{
			SAXParserFactory spf = SAXParserFactory.newInstance();
			SAXParser sp = spf.newSAXParser();
			XMLReader xr = sp.getXMLReader();
			xr.setContentHandler(this);
			xr.parse(inputSource);
		}
		catch (Exception e)
		{
			throw new SVGException(e);
		}
	}

	@Override
	public void error(SAXParseException e) throws SAXException
	{
		log.error("doc error", e);
	}

	@Override
	public void startDocument() throws SAXException
	{
		if (log.isDebugEnabled())
			log.debug("Start doc");
		buildStack.clear();
	}

	@Override
	public void endDocument() throws SAXException
	{
		if (log.isDebugEnabled())
			log.debug("End doc");
	}

	@Override
	public void startElement(String namespaceURI, String sName, String qName, Attributes attrs) throws SAXException
	{
		if (log.isDebugEnabled())
		{
			// log.debug(printIndent(indent, " ") + "Starting parse of tag " + sName + ": " + namespaceURI);
		}
		if (skipNonSVGTagDepth != 0 || (!namespaceURI.equals("") && !namespaceURI.equals(SVGElement.SVG_NS)))
		{
			skipNonSVGTagDepth++;
			return;
		}
		sName = sName.toLowerCase();
		Object obj = nodeClasses.get(sName);
		try
		{
			SVGElement svgEle = null;
			if (obj == null)
			{
				svgEle = new SVGElement();
			}
			else
			{
				Class<?> cls = (Class<?>) obj;
				svgEle = (SVGElement) cls.newInstance();
			}
			SVGElement parent = null;
			if (buildStack.size() != 0)
				parent = (SVGElement) buildStack.getLast();
			svgEle.loaderStartElement(attrs, parent);

			buildStack.addLast(svgEle);
		}
		catch (Exception e)
		{
			e.printStackTrace();
			throw new SAXException(e);
		}

	}

	@Override
	public void endElement(String namespaceURI, String sName, String qName) throws SAXException
	{
		if (log.isDebugEnabled())
		{
			// log.debug(printIndent(indent, " ") + "Ending parse of tag " + sName + ": " + namespaceURI);
		}

		if (skipNonSVGTagDepth != 0)
		{
			skipNonSVGTagDepth--;
			return;
		}

		sName = sName.toLowerCase();

		// Object obj = nodeClasses.get(sName);
		// if (obj == null)
		// return;

		try
		{
			SVGElement svgEle = (SVGElement) buildStack.removeLast();
			SVGElement parent = null;
			if (buildStack.size() != 0)
			{
				parent = (SVGElement) buildStack.getLast();
			}
			// else loadRoot = (SVGElement)svgEle;

			if (parent != null)
			{
				parent.loaderAddChild(svgEle);
			}
			else
			{
				this.root = (SVGRoot) svgEle;
			}
			svgEle.loaderEndElement();
		}
		catch (Exception e)
		{
			e.printStackTrace();
			throw new SAXException(e);
		}
	}

	@Override
	public void characters(char buf[], int offset, int len) throws SAXException
	{
		if (skipNonSVGTagDepth != 0)
		{
			return;
		}
		if (buildStack.size() != 0)
		{
			SVGElement parent = (SVGElement) buildStack.getLast();
			String s = new String(buf, offset, len);
			parent.loaderAddText(s);
		}
	}
}